﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using System.Threading.Tasks;
using StyleMVVM.Data.Activation;

namespace StyleMVVM.DependencyInjection.Impl
{
	public sealed class GenericExportStrategy :
		IGenericActivationStrategy, IInstanceExportStrategy
	{
		private volatile List<IExportCondition> conditions;
		private volatile List<IExportPipelineHandler> pipelineHandlers;
		private volatile IEnumerable<KeyValuePair<string, object>> metadata;
		private object syncObject = new object();
		private MethodInfo internalInfo;
		private bool suspend;
		private bool registerHandlers;

		public GenericExportStrategy(
			IEnumerable<string> exportNames, Type activationType)
		{
			ActivationType = activationType;

			ExportNames = exportNames;

			Environment = ExportEnvironment.Any;
		}

		public IDependencyInjectionContainer Container { get; set; }

		public IActivationService ActivationService { get; set; }

		public IEnumerable<string> ExportNames { get; set; }

		public IEnumerable<Type> ExportedTypes { get; set; }

		public Type ActivationType { get; private set; }

		public bool ExternallyOwned { get; set; }
		public BeforeDisposalCleanupDelegate DisposalCleanupDelegate { get; set; }

		public ILifeCycleContainer LifeCycleContainer { get; set; }

		public bool Suspendable { get; set; }

		public ExportEnvironment Environment { get; set; }

		public int Priority { get; set; }

		public bool IsCoreExport { get; set; }

		public int CoreExportStartOrder { get; set; }

		public void Initialize()
		{
		}

		public void AddPiplineHandler(IExportPipelineHandler pipelineHandler)
		{
			lock (syncObject)
			{
				List<IExportPipelineHandler> tempPipelines = new List<IExportPipelineHandler>();

				if (pipelineHandlers != null)
				{
					tempPipelines.AddRange(pipelineHandlers);
				}

				tempPipelines.Add(pipelineHandler);

				pipelineHandlers = tempPipelines;
			}
		}

		public void RemovePipelineHandler(IExportPipelineHandler pipelineHandler)
		{
			lock (syncObject)
			{
				if (pipelineHandlers != null)
				{
					List<IExportPipelineHandler> tempHandlers = new List<IExportPipelineHandler>(pipelineHandlers);

					tempHandlers.Remove(pipelineHandler);

					if (tempHandlers.Count == 0)
					{
						pipelineHandlers = null;
					}
					else
					{
						pipelineHandlers = tempHandlers;
					}
				}
			}
		}

		public bool MeetsConditions(IDependencyInjectionContainer container, IInjectionContext injectionContext)
		{
			List<IExportCondition> localConditions = conditions;

			if (localConditions == null)
			{
				return true;
			}

			foreach (IExportCondition exportCondition in localConditions)
			{
				if (!exportCondition.ConditionMeet(container, injectionContext, this))
				{
					return false;
				}
			}

			return true;
		}

		public void AddCondition(IExportCondition condition)
		{
			lock (syncObject)
			{
				List<IExportCondition> localConditions = new List<IExportCondition> { condition };

				if (conditions != null)
				{
					localConditions.AddRange(conditions);
				}

				conditions = localConditions;
			}
		}

		public void RemoveCondition(IExportCondition condition)
		{
			lock (syncObject)
			{
				if (conditions != null)
				{
					List<IExportCondition> localConditions = new List<IExportCondition>(conditions);

					localConditions.Remove(condition);

					if (localConditions.Count != 0)
					{
						conditions = localConditions;
					}
					else
					{
						conditions = null;
					}
				}
			}
		}

		public IEnumerable<IExportCondition> Conditions()
		{
			List<IExportCondition> localConditions = conditions;

			if (localConditions == null)
			{
				return new IExportCondition[] { };
			}

			return localConditions.ToArray();
		}

		public IEnumerable<KeyValuePair<string, object>> Metadata
		{
			get
			{
				if (metadata != null)
				{
					return metadata;
				}

				return new KeyValuePair<string, object>[0];
			}
			set { metadata = value; }
		}

		public IEnumerable<EnrichWithDelegate> EnrichWithDelegates { get; set; }

		public bool DiscoverAttributes { get; set; }

		public IEnumerable<InitializePropertyInfo> InitializeProperties { get; set; }

		public IEnumerable<ImportPropertyInfo> ImportProperties { get; set; }

		public IEnumerable<ImportMethodInfo> ImportMethods { get; set; }

		public string ActivationMethod { get; set; }

		public object ConstructorInfo { get; set; }

		public object Activate(IInjectionContext injectionContext, [ReadOnlyArray] object[] constructorParams)
		{
			throw new NotImplementedException();
		}

		public IExportStrategy CreateClosedStrategy(Type tType)
		{
			Type activateType = ActivationType.MakeGenericType(tType.GetTypeInfo().GenericTypeArguments);

			InstanceExportStrategy returnValue = new InstanceExportStrategy(activateType);

			returnValue.DiscoverAttributes = DiscoverAttributes;
			returnValue.ImportMethods = ImportMethods;
			returnValue.ImportProperties = ImportProperties;
			returnValue.ImportMethods = ImportMethods;
			returnValue.ConstructorInfo = ConstructorInfo;
			returnValue.Environment = Environment;
			returnValue.Priority = Priority;
			returnValue.ExportedTypes = new[] { tType };
			returnValue.ExportNames = new[] { tType.FullName };
			returnValue.ActivationService = ActivationService;
			returnValue.ActivationMethod = ActivationMethod;
			returnValue.EnrichWithDelegates = EnrichWithDelegates;

			if (ImportProperties != null)
			{
				FindClosedPropertyInfos(activateType, returnValue);
			}

			if (ConstructorInfo != null)
			{
				FindClosedConstructorInfo(activateType, returnValue);
			}

			returnValue.Initialize();

			var tempList = pipelineHandlers;

			if (tempList != null)
			{
				foreach (IExportPipelineHandler pipelineHandler in tempList)
				{
					returnValue.AddPiplineHandler(pipelineHandler);
				}
			}

			var tempConditions = conditions;

			if (tempConditions != null)
			{
				foreach (IExportCondition exportCondition in tempConditions)
				{
					returnValue.AddCondition(exportCondition);
				}
			}

			return returnValue;
		}

		private void FindClosedConstructorInfo(Type activateType, InstanceExportStrategy returnValue)
		{
			ConstructorInfo info = ConstructorInfo as ConstructorInfo;

			if (info != null)
			{
				int numParams = info.GetParameters().Length;

				foreach (ConstructorInfo declaredConstructor in activateType.GetTypeInfo().DeclaredConstructors)
				{
					if (declaredConstructor.GetParameters().Length == numParams)
					{
						returnValue.ConstructorInfo = declaredConstructor;
						break;
					}
				}
			}
		}

		/// <summary>
		/// This method goes through the imported properties and finds the property info's for the closed type 
		/// The current property infos are open
		/// </summary>
		/// <param name="closedType"></param>
		/// <param name="exportStrategy"></param>
		/// <param name="propertyInfos"></param>
		private void FindClosedPropertyInfos(Type closedType, InstanceExportStrategy exportStrategy)
		{
			List<ImportPropertyInfo> importProperties = new List<ImportPropertyInfo>();

			foreach (ImportPropertyInfo importPropertyInfo in ImportProperties)
			{
				PropertyInfo propertyInfo = importPropertyInfo.PropertyInfo as PropertyInfo;

				if (propertyInfo != null)
				{
					ImportPropertyInfo newPropertyInfo = new ImportPropertyInfo
																		 {
																			 ExportActivator = importPropertyInfo.ExportActivator,
																			 ExportName = importPropertyInfo.ExportName,
																			 IsRequired = importPropertyInfo.IsRequired,
																			 IsVolatile = importPropertyInfo.IsVolatile,
																		 };

					newPropertyInfo.PropertyInfo =
						closedType.GetTypeInfo().GetDeclaredProperty(propertyInfo.Name);

					importProperties.Add(newPropertyInfo);
				}
			}

			exportStrategy.ImportProperties = importProperties;
		}
	}
}